﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
using Microsoft.Scripting;

namespace Warensoft.PythonExtension
{
    /// <summary>
    /// IronPython加载器
    /// </summary>
    /// <typeparam name="CallBackFunctionType">用于回调的委托类型</typeparam>
    public class IronPythonLoader<CallBackFunctionType>where CallBackFunctionType :class
    {
        private bool autoLoad = false;
        private FileSystemWatcher watcher = new FileSystemWatcher();
        /// <summary>
        /// 当脚本被加载时触发，只要脚本被修改就会触发
        /// </summary>
        public event EventHandler ScriptLoaded;

        /// <summary>
        /// 脚本被加载前触发
        /// </summary>
        public event EventHandler PreStartLoad;

        private ScriptEngine pythonEngine;
        /// <summary>
        /// 构造函数
        /// </summary>
        public IronPythonLoader(bool autoLoad)
        {
            this.pythonEngine =IronPython .Hosting. Python.CreateEngine();
            this.autoLoad = autoLoad;
            this.watcher.EnableRaisingEvents = false;
            this.watcher.Changed += new FileSystemEventHandler(watcher_Changed);
        }

        void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (this.autoLoad &&this.PreStartLoad != null)
            {
                this.PreStartLoad(this, new EventArgs());
                this.Load();
            }
           
        }

        
        void IronPythonLoader_PreStartLoad(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Python配置文件的名字
        /// </summary>
        public string ConfigPyFileName { get; set; }
        /// <summary>
        /// 设置或获取配置文件中的函数名
        /// </summary>
        public List<string> ConfigFunctionNames { get; set; }
        /// <summary>
        /// 获取配置文件中的函数列表
        /// </summary>
        public Dictionary<string, Func<string>> ConfigFunctionList
        {
            get
            {
                return this.configFunctionList;
            }
        }
        private Dictionary<string, Func<string>> configFunctionList = new Dictionary<string, Func<string>>();

        /// <summary>
        /// 设置或获取脚本文件所在的文件夹
        /// </summary>
        public string PlugInFolder { get; set; }
        /// <summary>
        /// 设置或获取脚本文件中，用于被回调的函数名称
        /// </summary>
        public string CallBackFunctionName { get; set; }
        private Dictionary<string, CallBackFunctionType> callBackList = new Dictionary<string, CallBackFunctionType>();
        /// <summary>
        /// 用于获取已经被加载的脚本回调函数的列表
        /// </summary>
        public Dictionary<string, CallBackFunctionType> CallBackList
        {
            get
            {
                return this.callBackList;
            }
        }
        /// <summary>
        /// 加载脚本文件，如何脚本文件被修改或者被删除，该函数也会被调用
        /// 该函数被调用后，会导致ScriptLoaded事件被触发
        /// </summary>
        public void Load()
        {
            if (this.PlugInFolder ==null ||this.PlugInFolder =="")
            {
                throw new Exception("请设置脚本文件路径！");
            }
            this.callBackList.Clear();
            this.configFunctionList.Clear();
            var files = Directory.GetFiles(this.PlugInFolder ,"*.py");
            foreach (var file in files)
            {
                StringBuilder builder = new StringBuilder();
               
                using (StreamReader reader=new StreamReader (file ))
                {
                    builder.Append(reader .ReadToEnd());
                }
                var script = this.pythonEngine.CreateScriptSourceFromString(builder.ToString(), SourceCodeKind.Statements);
                var compiledCode = script.Compile();
                compiledCode.Execute();
                //如果是设置了配置文件的名字，就将配置文件的函数单独存储
                if (this.ConfigPyFileName != null && this.ConfigPyFileName != "" && Path.GetFileName(file) == this.ConfigPyFileName)
                {
                    foreach (var fun in this.ConfigFunctionNames)
                    {
                        var callback = compiledCode.DefaultScope.GetVariable<Func<string>>(fun);
                        this.configFunctionList.Add(fun, callback);
                    }
                }
                else
                {
                    if (this.CallBackFunctionName ==null ||this.CallBackFunctionName =="")
                    {
                        throw new Exception("请配置回调函数名");
                    }
                    var callback = compiledCode.DefaultScope.GetVariable<CallBackFunctionType>(this.CallBackFunctionName);
                    this.callBackList.Add(Path.GetFileNameWithoutExtension(file), callback);
                }
               
                
            }
            this.watcher.Path = this.PlugInFolder;
            this.watcher.EnableRaisingEvents = true;
            if (this.ScriptLoaded!=null )
            {
                this.ScriptLoaded(this,new EventArgs ());
            }
        }
       
    }
}
